package com.tianji.remark.service.impl;

import com.tianji.api.dto.msg.LikedTimesDTO;
import com.tianji.common.autoconfigure.mq.RabbitMqHelper;
import com.tianji.common.constants.MqConstants;
import com.tianji.common.utils.CollUtils;
import com.tianji.common.utils.StringUtils;
import com.tianji.common.utils.UserContext;
import com.tianji.remark.constant.RedisConstnt;
import com.tianji.remark.domain.dto.LikeRecordFormDTO;
import com.tianji.remark.domain.po.LikedRecord;
import com.tianji.remark.mapper.LikedRecordMapper;
import com.tianji.remark.service.ILikedRecordService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.connection.RedisSetCommands;
import org.springframework.data.redis.connection.ReturnType;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
import org.apache.ibatis.annotations.Select;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;

/**
 * <p>
 * 点赞记录表 服务实现类
 * </p>
 *
 * @author hrz
 * @since 2025-08-30
 */
@Service
@RequiredArgsConstructor
@Slf4j
@SuppressWarnings("all")
public class LikedRecordServiceImpl extends ServiceImpl<LikedRecordMapper, LikedRecord> implements ILikedRecordService {
    private final StringRedisTemplate redisTemplate;
    private final RabbitMqHelper rabbitMqHelper;

    @Override
    public void likedRecord(LikeRecordFormDTO dto) {
        //1.获取用户id
        Long user = UserContext.getUser();
        String str = user.toString();
        //2.判断是否是点赞
        boolean flag = dto.getLiked() ? liked(dto,str) : unliked(dto,str);
        //3.如果操作失败
        if(!flag){
            log.error("操作失败!业务id:{}",dto.getBizId());
            return;
        }
        //4.操作成功了,就统计点赞数量并更新到redis中
        //4.1组装key
        String key = RedisConstnt.LIKE_BIZ_KEY_PREFIX + dto.getBizId();
        //4.2统计基数
        Long size = redisTemplate.opsForSet().size(key);
        if(size == null) {return;}
        //4.3更新
        String key2 = RedisConstnt.LIKE_COUNT_KEY_PREFIX + dto.getBizType();
        Boolean add = redisTemplate.opsForZSet()
                .add(key2, dto.getBizId().toString(), size.doubleValue());
        if(!add){
            log.error("更新失败,业务id:{}",dto.getBizId());
            return;
        }
        //4.4更新成功
        log.info("更新成功,业务id:{}",dto.getBizId());
    }

    @Override
    public Set<Long> queryLikedTimes(List<Long> bizIds) {
        //1.todo 用管道 批量执行当前用户对业务的点赞情况
        Long userid = UserContext.getUser();
        if(userid == null){
            return new HashSet<>();
        }
        List<Object> objects = redisTemplate.executePipelined(new RedisCallback<Long>() {
            @Override
            public Long doInRedis(RedisConnection connection) throws DataAccessException {
                //1.循环装配命令到管道
                RedisSetCommands redisSetCommands = connection.setCommands();//获取set命令
                for (Long bizId : bizIds) {
                    //1.1组装key - 前缀加上业务id加上类型
                    String key = RedisConstnt.LIKE_BIZ_KEY_PREFIX + bizId;
                    //lua脚本
                    String luaScript = "local a = redis.call('sismember', KEYS[1], ARGV[1]) " +
                            "local b = redis.call('sismember', KEYS[1], ARGV[2]) " +
                            "return a + b > 0";
                    //1.2添加命令到管道
                    connection.eval(luaScript.getBytes(),
                            ReturnType.INTEGER,
                            1,  // KEYS数量
                            key.getBytes(),  // KEYS[1]
                            (userid + ":QA").getBytes(), (userid + ":NOTE").getBytes());
                }
                //忽略返回值,用封装到objects中的值
                return null;
            }
        });
        //2.根据返回的结果进行封装并返回
        //todo 注意,lua脚本会将true转换为1,false转换为0
        Set<Long> set = new HashSet<Long>();
        int index = 0;
        for (Long bizId : bizIds) {
            Object o = objects.get(index);
            if(o instanceof Long && (Long) o == 1) {
                //o等于1说明用户点赞了
                set.add(bizId);
            }
            index++;
        }
        return set;
    }

    @Override
    public void readLikeTiemsAndSendMessage(String bizType, Integer maxCount) {
        //1.组合key并查询数据
        String key = RedisConstnt.LIKE_BIZ_KEY_PREFIX + bizType;
        Set<ZSetOperations.TypedTuple<String>> typedTuples = redisTemplate.opsForZSet()
                .popMin(RedisConstnt.LIKE_BIZ_KEY_PREFIX + bizType, maxCount);
        //2.判断是否为空
        if(CollUtils.isEmpty(typedTuples)){
            return;
        }
        //3.不为空就转换数据类型并发送消息
        List<LikedTimesDTO> list = new ArrayList<LikedTimesDTO>();
        for (ZSetOperations.TypedTuple<String> typedTuple : typedTuples) {
            Double score = typedTuple.getScore();//点赞数
            String value = typedTuple.getValue();//业务id
            LikedTimesDTO likedTimesDTO = new LikedTimesDTO(Long.parseLong(value),score.intValue());
            //4.发送消息
            log.info("发送消息,业务id:{},点赞数:{}",value,score);
            list.add(likedTimesDTO);
        }
        //4.发送消息
        //组合key
        String routingKey = StringUtils.format(MqConstants.Key.LIKED_TIMES_KEY_TEMPLATE,bizType);
        rabbitMqHelper.send(
                MqConstants.Exchange.LIKE_RECORD_EXCHANGE,
                routingKey,
                list
        );
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteLiked(Long id) {
        this.baseMapper.deleteLiked(id);
    }

    private boolean liked(LikeRecordFormDTO dto, String user) {
        //1.根据业务id和用户id去redis查看是否点过赞了
        //2.组装key - 前缀加上业务id
        String key = RedisConstnt.LIKE_BIZ_KEY_PREFIX + dto.getBizId();
        //3.查看redis
        Boolean member = redisTemplate.opsForSet().isMember(key, user+":"+dto.getBizType());
        //4.判断是否存在
        if(member){
            //4.1存在,就直接返回
            return false; //本次操作无效
        }else {
            //4.2不存在,就添加
            Long add = redisTemplate.opsForSet().add(key, user+":"+dto.getBizType());
            if(add == 0){
                log.error("添加失败,业务id:{}",dto.getBizId());
                return false;
            }
            return true;
        }
    }

    private boolean unliked(LikeRecordFormDTO dto, String user){
        //1.根据业务id和用户id去redis查看是否点过赞了
        //2.组装key - 前缀加上业务id
        String key = RedisConstnt.LIKE_BIZ_KEY_PREFIX + dto.getBizId();
        //3.查看redis
        Boolean member = redisTemplate.opsForSet().isMember(key, user+":"+dto.getBizType());
        //4.判断是否存在
        if(!member){
            //4.1不存在,就直接返回
            return false; //本次操作无效
        }else {
            //4.2存在,就移除元素
            Long add = redisTemplate.opsForSet().remove(key, user+":"+dto.getBizType());
            if(add == 0){
                log.error("删除失败,业务id:{}",dto.getBizId());
                return false;
            }
            return true;
        }
    }
}